// shared_mutex standard header
#pragma once
#ifndef _SHARED_MUTEX_
#define _SHARED_MUTEX_
#ifndef RC_INVOKED

 #ifdef _M_CEE
 static_assert(false, "<shared_mutex> is not supported when compiling with /clr or /clr:pure.");
 #endif /* _M_CEE */

#include <thr/xthreads.h>
#include <mutex>
#include <condition_variable>

 #pragma pack(push,_CRT_PACKING)
 #pragma warning(push,_STL_WARNING_LEVEL)
 #pragma warning(disable: _STL_DISABLED_WARNINGS)
 #pragma push_macro("new")
 #undef new

 #ifndef _HAS_SHARED_MUTEX
  #ifdef _USING_V110_SDK71_
   #define _HAS_SHARED_MUTEX	0
  #else /* _USING_V110_SDK71_ */
   #define _HAS_SHARED_MUTEX	1
  #endif /* _USING_V110_SDK71_ */
 #endif /* _HAS_SHARED_MUTEX */

_STD_BEGIN
#if _HAS_SHARED_MUTEX
	// CLASS shared_mutex
class shared_mutex
	{	// class for mutual exclusion shared across threads
public:
	typedef _Smtx_t * native_handle_type;

	shared_mutex() _NOEXCEPT
		: _Myhandle(0)
		{	// default construct
		}

	~shared_mutex() _NOEXCEPT
		{	// destroy the object
		}

	void lock() _NOEXCEPT
		{	// lock exclusive
		_Smtx_lock_exclusive(&_Myhandle);
		}

	bool try_lock() _NOEXCEPT
		{	// try to lock exclusive
		return (_Smtx_try_lock_exclusive(&_Myhandle) != 0);
		}

	void unlock() _NOEXCEPT
		{	// unlock exclusive
		_Smtx_unlock_exclusive(&_Myhandle);
		}

	void lock_shared() _NOEXCEPT
		{	// lock non-exclusive
		_Smtx_lock_shared(&_Myhandle);
		}

	bool try_lock_shared() _NOEXCEPT
		{	// try to lock non-exclusive
		return (_Smtx_try_lock_shared(&_Myhandle) != 0);
		}

	void unlock_shared() _NOEXCEPT
		{	// unlock non-exclusive
		_Smtx_unlock_shared(&_Myhandle);
		}

	native_handle_type native_handle() _NOEXCEPT
		{	// get native handle
		return (&_Myhandle);
		}

	shared_mutex(const shared_mutex&) = delete;
	shared_mutex& operator=(const shared_mutex&) = delete;
private:
	_Smtx_t _Myhandle;
	};
#endif /* _HAS_SHARED_MUTEX */

	// CLASS shared_timed_mutex
class shared_timed_mutex
	{	// class for mutual exclusion shared across threads
	typedef unsigned int _Read_cnt_t;
	static constexpr _Read_cnt_t _Max_readers = _Read_cnt_t(-1);

public:
	shared_timed_mutex() _NOEXCEPT
		: _Mymtx(), _Read_queue(), _Write_queue(),
			_Readers(0), _Writing(false)
		{	// default construct
		}

	~shared_timed_mutex() _NOEXCEPT
		{	// destroy the object
		}

	void lock()
		{	// lock exclusive
		unique_lock<mutex> _Lock(_Mymtx);
		while (_Writing)
			_Write_queue.wait(_Lock);
		_Writing = true;
		while (0 < _Readers)
			_Read_queue.wait(_Lock);	// wait for writing, no readers
		}

	bool try_lock()
		{	// try to lock exclusive
		lock_guard<mutex> _Lock(_Mymtx);
		if (_Writing || 0 < _Readers)
			return (false);
		else
			{	// set writing, no readers
			_Writing = true;
			return (true);
			}
		}

	template<class _Rep,
		class _Period>
		bool try_lock_for(
			const chrono::duration<_Rep, _Period>& _Rel_time)
		{	// try to lock for duration
		return (try_lock_until(chrono::steady_clock::now() + _Rel_time));
		}

	template<class _Clock,
		class _Duration>
		bool try_lock_until(
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		{	// try to lock until time point
		auto _Not_writing = [this] { return (!_Writing); };
		auto _Zero_readers = [this] { return (_Readers == 0); };
		unique_lock<mutex> _Lock(_Mymtx);

		if (!_Write_queue.wait_until(_Lock, _Abs_time, _Not_writing))
			return (false);

		_Writing = true;

		if (!_Read_queue.wait_until(_Lock, _Abs_time, _Zero_readers))
			{	// timeout, leave writing state
			_Writing = false;
			_Lock.unlock();	// unlock before notifying, for efficiency
			_Write_queue.notify_all();
			return (false);
			}

		return (true);
		}

	void unlock()
		{	// unlock exclusive
			{	// unlock before notifying, for efficiency
			lock_guard<mutex> _Lock(_Mymtx);

			_Writing = false;
			}

		_Write_queue.notify_all();
		}

	void lock_shared()
		{	// lock non-exclusive
		unique_lock<mutex> _Lock(_Mymtx);
		while (_Writing || _Readers == _Max_readers)
			_Write_queue.wait(_Lock);
		++_Readers;
		}

	bool try_lock_shared()
		{	// try to lock non-exclusive
		lock_guard<mutex> _Lock(_Mymtx);
		if (_Writing || _Readers == _Max_readers)
			return (false);
		else
			{	// count another reader
			++_Readers;
			return (true);
			}
		}

	template<class _Rep,
		class _Period>
		bool try_lock_shared_for(
			const chrono::duration<_Rep, _Period>& _Rel_time)
		{	// try to lock non-exclusive for relative time
		return (try_lock_shared_until(_Rel_time
			+ chrono::steady_clock::now()));
		}

	template<class _Time>
		bool _Try_lock_shared_until(_Time _Abs_time)
		{	// try to lock non-exclusive until absolute time
		auto _Can_acquire = [this] {
			return (!_Writing && _Readers < _Max_readers); };

		unique_lock<mutex> _Lock(_Mymtx);

		if (!_Write_queue.wait_until(_Lock, _Abs_time, _Can_acquire))
			return (false);

		++_Readers;
		return (true);
		}

	template<class _Clock,
		class _Duration>
		bool try_lock_shared_until(
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		{	// try to lock non-exclusive until absolute time
		return (_Try_lock_shared_until(_Abs_time));
		}

	bool try_lock_shared_until(const xtime *_Abs_time)
		{	// try to lock non-exclusive until absolute time
		return (_Try_lock_shared_until(_Abs_time));
		}

	void unlock_shared()
		{	// unlock non-exclusive
		_Read_cnt_t _Local_readers;
		bool _Local_writing;

			{	// unlock before notifying, for efficiency
			lock_guard<mutex> _Lock(_Mymtx);
			--_Readers;
			_Local_readers = _Readers;
			_Local_writing = _Writing;
			}

		if (_Local_writing && _Local_readers == 0)
			_Read_queue.notify_one();
		else if (!_Local_writing && _Local_readers == _Max_readers - 1)
			_Write_queue.notify_all();
		}

	shared_timed_mutex(const shared_timed_mutex&) = delete;
	shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
private:
	mutex _Mymtx;
	condition_variable _Read_queue, _Write_queue;
	_Read_cnt_t _Readers;
	bool _Writing;
	};

	// TEMPLATE CLASS shared_lock
template<class _Mutex>
	class shared_lock
	{	// shareable lock
public:
	typedef _Mutex mutex_type;

	shared_lock() _NOEXCEPT
		: _Pmtx(0), _Owns(false)
		{	// default construct
		}

	explicit shared_lock(mutex_type& _Mtx)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(true)
		{	// construct with mutex and lock shared
		_Mtx.lock_shared();
		}

	shared_lock(mutex_type& _Mtx, defer_lock_t) _NOEXCEPT
		: _Pmtx(_STD addressof(_Mtx)), _Owns(false)
		{	// construct with unlocked mutex
		}

	shared_lock(mutex_type& _Mtx, try_to_lock_t)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared())
		{	// construct with mutex and try to lock shared
		}

	shared_lock(mutex_type& _Mtx, adopt_lock_t)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(true)
		{	// construct with mutex and adopt ownership
		}

	template<class _Rep,
		class _Period>
		shared_lock(mutex_type& _Mtx,
			const chrono::duration<_Rep, _Period>& _Rel_time)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared_for(_Rel_time))
		{	// construct with mutex and try to lock for relative time
		}

	template<class _Clock,
		class _Duration>
		shared_lock(mutex_type& _Mtx,
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		: _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared_until(_Abs_time))
		{	// construct with mutex and try to lock until absolute time
		}

	~shared_lock() _NOEXCEPT
		{	// destroy the lock
		if (_Owns)
			_Pmtx->unlock_shared();
		}

	shared_lock(shared_lock&& _Other) _NOEXCEPT
		: _Pmtx(_Other._Pmtx), _Owns(_Other._Owns)
		{	// construct by moving _Other
		_Other._Pmtx = 0;
		_Other._Owns = false;
		}

	shared_lock& operator=(shared_lock&& _Right) _NOEXCEPT
		{	// copy by moving _Right
		if (_Owns)
			_Pmtx->unlock_shared();
		_Pmtx = _Right._Pmtx;
		_Owns = _Right._Owns;
		_Right._Pmtx = 0;
		_Right._Owns = false;
		return (*this);
		}

	shared_lock(const shared_lock&) = delete;
	shared_lock& operator=(const shared_lock&) = delete;

	void lock()
		{	// lock the mutex
		_Validate();
		_Pmtx->lock_shared();
		_Owns = true;
		}

	bool try_lock()
		{	// try to lock the mutex
		_Validate();
		_Owns = _Pmtx->try_lock_shared();
		return (_Owns);
		}

	template<class _Rep,
		class _Period>
		bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time)
		{	// try to lock the mutex for _Rel_time
		_Validate();
		_Owns = _Pmtx->try_lock_shared_for(_Rel_time);
		return (_Owns);
		}

	template<class _Clock,
		class _Duration>
		bool try_lock_until(
			const chrono::time_point<_Clock, _Duration>& _Abs_time)
		{	// try to lock the mutex until _Abs_time
		_Validate();
		_Owns = _Pmtx->try_lock_shared_until(_Abs_time);
		return (_Owns);
		}

	void unlock()
		{	// try to unlock the mutex
		if (!_Pmtx || !_Owns)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::operation_not_permitted));
		_Pmtx->unlock_shared();
		_Owns = false;
		}

	// MUTATE
	void swap(shared_lock& _Right) _NOEXCEPT
		{	// swap with _Right
		_STD swap(_Pmtx, _Right._Pmtx);
		_STD swap(_Owns, _Right._Owns);
		}

	mutex_type *release() _NOEXCEPT
		{	// release the mutex
		_Mutex *_Res = _Pmtx;
		_Pmtx = 0;
		_Owns = false;
		return (_Res);
		}

	// OBSERVE
	bool owns_lock() const _NOEXCEPT
		{	// return true if this object owns the lock
		return (_Owns);
		}

	explicit operator bool() const _NOEXCEPT
		{	// return true if this object owns the lock
		return (_Owns);
		}

	mutex_type *mutex() const _NOEXCEPT
		{	// return pointer to managed mutex
		return (_Pmtx);
		}

private:
	_Mutex *_Pmtx;
	bool _Owns;

	void _Validate() const
		{	// check if the mutex can be locked
		if (!_Pmtx)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::operation_not_permitted));
		if (_Owns)
			_THROW_NCEE(system_error,
				_STD make_error_code(errc::resource_deadlock_would_occur));
		}
	};

	// SWAP
template<class _Mutex>
	void swap(shared_lock<_Mutex>& _Left,
		shared_lock<_Mutex>& _Right) _NOEXCEPT
	{	// swap two shared locks
	_Left.swap(_Right);
	}
_STD_END
 #pragma pop_macro("new")
 #pragma warning(pop)
 #pragma pack(pop)
#endif /* RC_INVOKED */
#endif /* _SHARED_MUTEX_ */

/*
 * Copyright (c) by P.J. Plauger. All rights reserved.
 * Consult your license regarding permissions and restrictions.
V6.50:0009 */
